/************************************************************************
Copyright 2003, 2004, 2005, 2006, 2007, 2008 Grid Technology Research Center,
National Institute of Advanced Industrial Science and Technology.

Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at

    http://www.apache.org/licenses/LICENSE-2.0

Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
************************************************************************/

package jp.aist.gtrc.plus.reserve;

import java.util.Calendar;
import java.util.Locale;

import jp.aist.gtrc.plus.scheduler.node.ResourceOption;
import jp.aist.gtrc.plus.scheduler.util.TextUtil;
import jp.aist.gtrc.plus.scheduler.util.TimeUtil;

public class ReserveStatus extends ReserveRequest implements Comparable<ReserveStatus> {

	private static final long serialVersionUID = 7728734329802265868L;

	/*
	 * state flow chart:
	 *   init --> CONFIRMED --> RUNNING --> DONE
	 *     				|           |           |
	 *    				V           V           V
	 *            CANCEL or ERROR      same as left
	 */
	public enum State {
		Confirmed, Running, Done, Canceled, Destroying, Error
	};

	private ReserveId id;
	private State state;
	private boolean waitCommit;
	private String[] rsvNodes;

	public final String getStateStr() {

		if (waitCommit) {
			return "Waiting Commit";
		}

		updateReserveState();

		return state.name();

	}

	private void init(ReserveId rsvId, State state, String[] rsvNodes) throws ReserveException{

		this.id = rsvId;
		this.state = state;
		this.waitCommit = false;
		this.rsvNodes = (rsvNodes != null) ? rsvNodes : new String[]{};

	}

	public ReserveStatus(ReserveId rsvId, ReserveRequest rsvReq, State state) throws ReserveException{

		super(rsvReq);
		init(rsvId, state, null);

	}

	public ReserveStatus(ReserveId rsvId, ReserveRequest rsvReq, String[] rsvNodes) throws ReserveException{

		super(rsvReq);
		init(rsvId, State.Confirmed, rsvNodes);

	}

	public final ReserveRequest getReserveRequest() {

		return (ReserveRequest)this;

	}

	void setReserveState(State newState){

		this.state = newState;

	}

	private void updateReserveState() {

		switch(state){
		case Confirmed:
			Calendar now = Calendar.getInstance();
			if (now.after(getStartTime())) {
				if (now.before(getEndTime()))
					setReserveState(State.Running);
				else
					setReserveState(State.Done);
			}
			break;
		case Running:
		case Canceled:
			if (Calendar.getInstance().after(getEndTime()))
				setReserveState(State.Done);
			break;
		case Done:
		case Destroying:
		case Error:
			// don't change state more
			break;
		default:
			assert(false);
			break;
		}

	}

	void setWaitCommit() {

		assert(waitCommit == false);
		waitCommit = true;

	}

	void clearWaitCommit() {

		assert(waitCommit == true);
		waitCommit = false;

	}

	protected void setReservedNodes(String[] nodeNames){

		rsvNodes = (nodeNames != null) ? nodeNames : new String[]{};

	}

	public ReserveId getReserveId() {
		return id;
	}

	public State getReserveState() {

		updateReserveState();
		return state;

	}

	public String[] getReservedNodeNames() {
		return TextUtil.cloneStringArray(rsvNodes);
	}

	public String getOptionsInLine() {
		ResourceOption opt = getOptions();
		String s1 = TextUtil.fromMap(opt.getDefaultOptions(), ",");
		String s2 = TextUtil.fromMap(opt.getResourceOptions(), ",");
		if ((s1.length() > 0) && (s2.length() > 0)) {
			return s1 + " " + s2;
		} else {
			return s1 + s2;
		}
	}

	private String toString(String attr, String value) {

		return attr + "\t" + value + "\n";

	}

	public int compareTo(ReserveStatus o) {

		ReserveStatus other = (ReserveStatus)o;
		return this.getReserveId().compareTo(other.getReserveId());

	}

	public boolean equals(Object o) {

		if (o instanceof ReserveStatus) {
			ReserveStatus other = (ReserveStatus)o;
			return this.getReserveId().equals(other.getReserveId());
		} else {
			assert(false);
			return false;
		}

	}

	public int hashCode() {

		return id.hashCode();

	}

	public String toString() {

		int diff = TimeUtil.calToSec(getEndTime()) - TimeUtil.calToSec(getStartTime());

		return toString("id", id.toString())
			+ toString("owner", getOwner())
			+ toString("users", TextUtil.fromArray(getUsers(), " "))
			+ toString("start", String.format(Locale.US, "%tc", getStartTime()))
			+ toString("end", String.format(Locale.US, "%tc", getEndTime()))
			+ toString("duration", Integer.toString(diff) + "[sec]")
			+ toString("state", getStateStr())
			+ toString("nodes", TextUtil.fromArray(getReservedNodeNames(), " "))
			+ toString("options", getOptionsInLine());

	}

}
